<?php
/**
 * 将CSV格式的文本转换为PHP数组的类
 *
 * @author ananda
 */
class Converter_Csv2Array extends EmptyIterator
{
    /**
     * 当前读取的条目数据
     * @var string
     */
    public $current_id = null;

    /**
     * 要读取的文件名
     * @var string
     */
    private $filename;

    /**
     * 文件句柄
     * @var Resource
     */
    private $handle = false;

    /**
     * 每一行的数据
     * @var array
     */
    private $row;

    /**
     * 用于描述转换时每一行的格式的数组
     * @var array
     */
    private $rules = array();

    /**
     * 当前读取行数
     * @var integer
     */
    private $line;

    /**
     * 设置ID键值位于解析出来的数组的哪个键值中
     * @var integer
     */
    private $id_offset;

    /**
     * 用于描述需要被忽略掉的行数的数组
     * @var array
     */
    private $ignore;

    /**
     * 指针
     * @var integer
     */
    private $cursor = 0;

    /**
     * 构造器
     * @param string $data CSV文本数据
     * @param array $rules 每一行的格式描述数组
     */
    public function __construct($filename, $id_offset, $rules = null) {
        $this->filename  = $filename;
        $this->id_offset = $id_offset;
        $rules && ($this->rules = $rules);
    }

    public function __destruct() {
        $this->handle && fclose($this->handle);
    }

    /**
     * 设置需要忽略掉的行数
     * @param integer $line
     */
    public function ignore($line)
    {
        $this->ignore[$line] = true;
    }

    public function process($row)
    {
        $record = array();

        foreach ($this->rules as $key => $rule) {
            if (!isset($row[$key])) {
                continue;
            }
            $record[$rule['name']] = $value = $this->format($row[$key], $rule);
            if ($key == $this->id_offset) {
                if (!$value) {
                    return false;
                }
                $this->current_id = $value;
            }
        }
        return $record;
    }

    private function format($value, $rule)
    {
        $value = trim($value);
        switch ($rule['type']) {
            case 'integer':
                $value = intval($value);
                break;
            case 'string':
            default:
                $value = strval($value);
                break;
        }
        return $value;
    }

    /**
     * 迭代器接口
     * @return mixed
     */
    public function current()
    {
        return $this->process($this->row);
    }

    /**
     * 迭代器接口
     * @return mixed
     */
    public function key()
    {
        return $this->current_id;
    }

    /**
     * 迭代器接口
     * @return void
     */
    public function next()
    {
    }

    /**
     * 迭代器接口
     * @return void
     */
    public function rewind()
    {
        if ($this->handle) {
            fclose($this->handle);
        }

        // 打开文件准备处理
        if (file_exists($this->filename)) {
            $this->handle = fopen($this->filename, 'r');
        }
        $this->line = 0;
    }

    /**
     * 迭代器接口
     * @return boolean
     */
    public function valid()
    {
        if (!$this->handle) {
            return false;
        }
        $this->current_id = null;

        $this->row = $this->getRow();
        if (feof($this->handle)) {
            fclose($this->handle);
            return false;
        }

        while (isset($this->ignore[$this->line]) || !$this->row) {
            $this->line++;
            $this->row = $this->getRow();
            if (feof($this->handle)) {
                fclose($this->handle);
                return false;
            }
        }
        $this->line++;
        return true;
    }

    private function getRow()
    {
        $row = fgetcsv($this->handle, 4096, ',', '"', '\\');
        if (!$row) {
            return $row;
        }
        if (count($row) == 1 && $row[0] == null) {
            return false;
        }
        return $row;
    }

}
